﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;

namespace Kea
{
    public abstract class DataPlanBase
    {
        public abstract int Count();
        public abstract bool Any();
    }
    public abstract class DataPlanBase<TE> : DataPlanBase
    {
        internal KeyComparerProvider<TE> Comparer { set; get; }
        internal LinkedFilter<TE> Filters { get; set; }

        protected abstract IEnumerable<TE> SelectInternal();
        private IEnumerable<TE> SelectWhere()
        {
            foreach (TE t in SelectInternal()) if (Filters.Where(t)) yield return t;
        }

        protected IEnumerable<TResult> Select<TResult>(Func<TE, TResult> selector)
        {
            IEnumerable<TE> rn = (Filters == null) ? SelectInternal() : SelectWhere();
            if (Comparer != null)
                return GetOrdered(rn, selector);
            else
                return rn.Select(selector);
        }

        public sealed override int Count()
        {
            return Select(q => q).Count();
        }

        public sealed override bool Any()
        {
            return Select(q => q).Any();
        }

        private IEnumerable<TResult> GetOrdered<TResult>(IEnumerable<TE> rn, Func<TE, TResult> selector)
        {
            List<Orderable<TResult>> ol;
            var compar = Comparer.CreateProvider();
            using (IEnumerator<TE> er = rn.GetEnumerator())
            {
                if (!er.MoveNext()) yield break;//this should return.

                ol = new List<Orderable<TResult>>();
                int j = 0;
                do
                {
                    TE t = er.Current;
                    ol.Add(new Orderable<TResult>() { Index = j++, ToOrder = selector(t) });
                    compar.ReadKey(t);
                } while (er.MoveNext());
            }
            
            ol.Sort((o1, o2) => compar.Compare(o1.Index, o2.Index));
            compar = null;

            for (int i = 0; i < ol.Count; i++)
            {
                var oli = ol[i];
                yield return oli.ToOrder;
                ol[i] = default(Orderable<TResult>);//freeing this up early for GC
            }
        }


        private struct Orderable<TResult>
        {
            public int Index { get; set; }
            public TResult ToOrder;
        }


    }
}
